#include "connection.h"
#include <iostream>
#include <string>
#include "http/server.h"
#include "loop.h"
#include "Router.h"

using namespace std;


Connection::Connection(Server *svr)
        : asocket(-1), head_buffer(max_header_size, 0), svr(svr), request(*this) {
}

void Connection::connect() {
    aio_init(&sock);
    _closing = false;
    sock.set_block(false);
    loop->add_socket(this);

    head_buffer.clear();
    head_buffer.resize(head_buffer.capacity());
    coro.reset(handle());
}

async<> Connection::handle() {
    //! parse http head
    string &str = head_buffer;
    int ret, count = 0;
    size_t pos = string::npos;
    do {
        ret = co_await read(str.data() + count, str.capacity() - count);
        cout << fileno() << " " << ret << " " << count << endl;
        count += ret;
        if(ret == 0 || count == str.capacity()) break;
        pos = str.find("\r\n\r\n");
    } while (pos == string::npos);
    if(pos == string::npos) {
        string response;
        response = render("http header is too long, max length required is 4096", 400);
        co_await write(response);
        on_close();
        co_return;
    }else
        str.resize(pos + 4);
    parse_head(str.substr(0, pos), head);

    //! get view from route
    View* view = router.match(head.path.c_str());
    if(!view) {
        std::string res("No Router Match Url: ");
        res += head.path;
        res = render(res, 404);
        co_await write(res);
        co_return;
    }

    //! get response
    co_await view->dispatch(&request);

    //! return response
    on_close();
}

void Connection::closing() {
    //! take back connect handle
    if(svr) svr->valid_conn_indices.emplace_back(fileno());
}

void Connection::on_close() {
    if(!_closing) {
        _closing = true;
        loop->set_timeout(0, [this]() {
            closing();
            while(read.waiting() || write.waiting()) {
                read.stop(), write.stop();
                coro.resume();
            }
            coro.reset();
            loop->rmv_socket(sock.fileno());
            sock.shutdown(SHUT_RDWR);
            cout << fileno() << "shut down" << endl;
        });
    }
}
